#pragma once
#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <stack>
#include <list>
#include <set>
#include <utility>
#include <sstream>

#include <opencv.hpp>
#include <cv.h>
#include <cxcore.h>
#include <highgui.h>

using namespace std;

namespace Tools
{
	class LinkedList;
	struct ImagePointComp {
		bool operator () ( const CvPoint & lhs, const CvPoint & rhs )
		{
			if (lhs.y < rhs.y)
			{
				return true;
			}
			else if (lhs.y > rhs.y)
			{
				return false;
			}
			else if (lhs.x < rhs.x)
			{
				return true;
			}
			else
			{
				return false;
			}
		}
	};

	bool point_compare (const CvPoint & lhs, const CvPoint & rhs);

	struct StrokeNode
	{

		set<CvPoint,ImagePointComp> elements;
		map<StrokeNode*, vector<CvPoint>> spwt;
		vector<CvPoint> polygon_vertex;
		bool isAmbigousZone;
		vector<StrokeNode*> neighbours;
		map<StrokeNode*,pair<CvPoint,CvPoint>> starting_pointers;
		int id;
		bool isolated;
		StrokeNode()
		{
			this->isAmbigousZone = false;
			this->isolated = true;
			this->elements.clear();
			this->spwt.clear();
			this->polygon_vertex.clear();
			
		}
		~StrokeNode()
		{
			this->elements.clear();
			this->spwt.clear();
			this->polygon_vertex.clear();
			this->neighbours.clear();
		}
		void create_link(StrokeNode *neighbour)
		{
			this->neighbours.push_back(neighbour);
		}
		void create_starting_pointers(CvPoint p1, CvPoint p2, StrokeNode* ambigous_zone)
		{
			pair<CvPoint,CvPoint> pointers = make_pair(p1,p2);
			this->starting_pointers[ambigous_zone] = pointers;
		}
		CvPoint gravity_center()
		{
			set<CvPoint,ImagePointComp>::iterator it;
			CvPoint retval;
			retval.x = 0;
			retval.y = 0;
			if (this->elements.size()> 0)
			{
				int count = 0;
				for(it = this->elements.begin(); it!= this->elements.end(); it++)
				{
					retval.x+= (*it).x;
					retval.y+= (*it).y;
					count++;
				}
			
				retval.x = retval.x / count;
				retval.y = retval.y / count;
			}

			return retval;
		}
	};

	struct NodeList
	{
		CvPoint data;
		int freeman_code;
		int index;
		NodeList* next;
		NodeList* prev;
		LinkedList* owner;
		NodeList()
		{
			index = 0;
			next = NULL;
			prev = NULL;
		}
	};

	struct Vertex
	{
		int x;
		int y;
		bool traversed;
		set<Vertex*> links;
		Vertex() 
		{
			x = 0;
			y = 0;
			traversed = false;
			links.clear();
		}
		bool operator==(const Vertex &lhs)
		{
			return ((this->x == lhs.x) && (this->y == lhs.y));
		}
		bool operator!=(const Vertex &lhs)
		{
			return ((this->x != lhs.x) || (this->y != lhs.y));
		}
	};
	
	struct VertexComp {
		bool operator () ( const Vertex & lhs, const Vertex & rhs )
		{
			if (lhs.y < rhs.y)
			{
				return true;
			}
			else if (lhs.y > rhs.y)
			{
				return false;
			}
			else if (lhs.x < rhs.x)
			{
				return true;
			}
			else
			{
				return false;
			}
		}
	};

	set<CvPoint,ImagePointComp> marking_borders(IplImage *source);
	bool check_border_condition(int** mask);
	double distance_Euclidean(CvPoint p1, CvPoint p2);
	double distance_point_to_line(CvPoint p0, CvPoint v0, CvPoint v1);
	set<CvPoint,ImagePointComp> get_substroke_elements(CvPoint first_point, IplImage* map, CvScalar color, CvScalar &combine_color = cvScalar(0,0,0));
	vector<vector<CvPoint>> create_segments(vector<CvPoint> points);
	void draw_stroke(const char* filename, const vector<StrokeNode*> &nodes);

}